#include "mouse.h"
#include <stdio.h>

int Mouse::read( )
{
    int data = 0;

    while( true )
    {
        data = IORD_32DIRECT( PS2_BASE, 0 );
        if( ( ( data >> 16) & 0xFFFF ) != 0 )
        {
            return data;
        }
    }
    
    return data;
}

int Mouse::read( const unsigned int& timeout )
{
    int data = 0;
    volatile unsigned int t = timeout;

    while( t-- )
    {
        data = IORD_32DIRECT( PS2_BASE, 0 );
        if( ( ( data >> 16) & 0xFFFF ) != 0 )
        {
            return data;
        }
    }
    
    // if non-valid data until timeout, return useless data
    return STATUS_ERROR;
}

void Mouse::write( int data )
{
    IOWR_8DIRECT( PS2_BASE, 0, data );
}

void Mouse::init( )
{
    write( COMMAND_RESET );
    read( );
    
    write( COMMAND_RESET );
    read( );
    
    write( COMMAND_RESET );
    read( );
    
    write( COMMAND_SET_SAMPLE_RATE );
    read( );
    
    write( 0xC8 );
    read( );
    
    write( COMMAND_SET_SAMPLE_RATE );
    read( );
    
    write( 0x64 );
    read( );
    
    write( COMMAND_SET_SAMPLE_RATE );
    read( );
    
    write( 0x50 );
    read( );
    
    write( COMMAND_GET_DEVICE_ID );
    read( );

    write( COMMAND_SET_RESOLUTION );
    read( );

    write( 0x01 );
    read( );
    
    write( COMMAND_SET_SCALING_1_TO_1 );
    read( );
    
    write( COMMAND_SET_SAMPLE_RATE );
    read( );
    
    write( 0xC8 );
    read( );

    write( COMMAND_SET_REMOTE_MODE );
    read( );

}

bool Mouse::update( const int& timeout )
{
    int x_sign = 0;
    int y_sign = 0;
    int data[ 10 ] = { 0, };

    // request position data
    IOWR_32DIRECT( PS2_BASE, 0, COMMAND_READ_DATA );

    // wait for ack
    while( read( ) & 0xFF != STATUS_ACK );

    // if new ack is received while receiving data, refresh
    int loop = 3;
    while( loop )
    {
        data[ 3 - loop ] = read( timeout );
        
        if( data[ 3 - loop ] == STATUS_ERROR )
        {
            return false;
        }
        if( ( unsigned char ) ( data[ 3 - loop ] & 0xFF ) == ( unsigned char ) STATUS_ACK )
        {
            loop = 3;
        }
        else
        {
            loop--;
        }
    }
    
    // set button
    if( data[ 0 ] & 0x01 == 1 )
    {
        m_status[ LEFT ] = PRESSED; 
    }
    else
    {
        m_status[ LEFT ] = RELEASED;
    }
    
    if( ( data[ 0 ] >> 1 ) & 0x01 == 1 )
    {
        m_status[ RIGHT ] = PRESSED;
    }
    else
    {
        m_status[ RIGHT ] = RELEASED;
    }
    
    // set position
    if( ( data[ 0 ] >> 4 ) & 0x01 == 1 )
    {
        x_sign = 1;
    }
    if( ( data[ 0 ] >>5 ) & 0x01 == 1 )
    {
        y_sign = 1;
    }
    
    if( x_sign == 1 )
    {
        m_dir_x = data[ 1 ] & 0xFF;
    }
    else
    {
        m_dir_x = data[ 1 ] & 0xFF;
    }
    
    if( y_sign == 0 )
    {
        m_dir_y = 0 - ( data[ 2 ] & 0xFF );
    }
    else
    {
        m_dir_y = ( ~( data[ 2 ] & 0xFF ) + 1 ) & 0xFF;
    }
    
    // remove mouse's pop data
    if( m_dir_x >= 6 )
    {
        m_dir_x = 0;
    }
    else if( m_dir_x <= -6 )
    {
        m_dir_x = 0;
    }
    
    if( m_dir_y >= 6 )
    {
        m_dir_y = 0;
    }
    else if( m_dir_y <= -6 )
    {
        m_dir_y = 0;
    }
    
    // increase mouse speed
    m_dir_x <<= 2;
    m_dir_y <<= 2;
    
    return true;
}

void Mouse::get_direction( int& x, int& y ) const
{
    x = m_dir_x;
    y = m_dir_y;
}

const Mouse::TYPE_BUTTON_STATUS Mouse::get_status( const TYPE_BUTTON& button ) const
{
    return m_status[ button ];
}
